/*
 * TAN-Manager
 * Copyright (C) 2011 Bernhard Bermeitinger
 * E-Mail: bernhard.bermeitinger@googlemail.com
 * Link:   https://code.google.com/p/tan-management/
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package tan_manager.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import tan_manager.Constants;
import tan_manager.datahandler.FileManager;
import tan_manager.gui.newfile.CreateNewFileFrame;

/**
 * Frame in which everything happens.
 *
 * @author Bernhard Bermeitinger
 */
public class TANFrame extends JFrame {

    /**
     * serialVersionUID; just for to get rid of the warning message
     */
    private static final long serialVersionUID = 1L;

    /**
     * Here are the radio buttons to choose the bank.
     */
    private final JPanel chooseBankPanel = new JPanel();

    /**
     * Here is the yes- and no-Button to finally delete the TAN from the file
     * - or not.
     */
    private final JPanel confirmTANPanel = new JPanel(new FlowLayout());

    /**
     * Contains the radiobuttons to choose the bank.
     */
    private final ArrayList<JRadioButton> chooseBankButtons = new ArrayList<JRadioButton>();

    /**
     * Shows how many TANs for the current chosen bank are already used.
     */
    private final JLabel validTANsLabel = new JLabel(Constants.textbundle.getString("NO_BANK_CHOSEN"));

    /**
     * Here will be the number which TAN you want.
     */
    private final JTextField numberField = new JTextField(Constants.DEFAULT_NUMBERFIELD);

    /**
     * Button for printing the corresponding TAN to the label.
     */
    private final JButton getTANButton = new JButton(Constants.textbundle.getString("GET_TAN"));

    /**
     * Contains the new TAN after getting it.
     */
    private final JTextField tanField = new JTextField(Constants.DEFAULT_TAN);

    /**
     * Label to ask user if the TAN worked.
     */
    private final JLabel askWorkingLabel = new JLabel(Constants.textbundle.getString("WORKING?"));

    /**
     * Button to confirm the TAN and delete it afterwards.
     */
    private final JButton yesButton = new JButton(Constants.textbundle.getString("YES"));

    /**
     * Button to confirm that the TAN didn't work, so don't delete it.
     */
    private final JButton noButton = new JButton(Constants.textbundle.getString("NO"));

    /**
     * Label at the bottom to tell user what happens.
     */
    private final JTextArea messageTextView = new JTextArea(Constants.textbundle.getString("TAN-MANAGER_STARTED"));

    /**
     * Provides the ability to get the TAN from the file.
     */
    private FileManager dataManager;

    /**
     * Contains the RadioButtons to choose the bank.
     */
    private ButtonGroup bankChooserGroup;

    /**
     * Menubar.
     */
    private JMenuBar menuBar = new JMenuBar();

    /**
     * Creates a new Frame with a radio button for every file ending with {@code Constants.FILE_ENDING} in the executing
     * folder.
     *
     * @param title Title of the window
     */
    public TANFrame(final String title) {
        super(title);

        /*
         * Add a JRadioButton for each file ending with Constants.FILE_ENDING in the program folder.
         * Each file should be a file with TANs.
         */
        File dir = new File(".");
        File files[] = dir.listFiles();

        for (int i = 0; i < files.length; ++i) {
            if (files[i].getName().endsWith(Constants.FILE_ENDING)) {
                JRadioButton jrb = new JRadioButton(files[i].getName());
                jrb.addActionListener(new RadioButtonListener());
                chooseBankButtons.add(jrb);
            }
        }
        JMenu programMenu = new JMenu(Constants.textbundle.getString("PROGRAM"));
        JMenuItem exitItem = new JMenuItem(Constants.textbundle.getString("EXIT"));
        exitItem.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                setVisible(false);
                dispose();
                System.exit(0);
            }
        });
        programMenu.add(exitItem);

        JMenu tanFileMenu = new JMenu(Constants.textbundle.getString("TAN_FILE_MENUBAR"));
        JMenuItem createTANFile = new JMenuItem(Constants.textbundle.getString("CREATE_NEW_TAN_FILE"));

        createTANFile.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                CreateNewFileFrame c = new CreateNewFileFrame();
                c.setVisible(true);
            }
        });

        tanFileMenu.add(createTANFile);

        menuBar.add(programMenu);
        menuBar.add(tanFileMenu);

    }

    /**
     * Adds all components to pane.
     * 
     * @param pane pane to add the components.
     */
    public final void addComponentsToPane(final Container pane) {
        pane.add(menuBar);
        pane.setLayout(new GridLayout(11, 1));

        // Choose the bank for the TANs
        bankChooserGroup = new ButtonGroup();
        for (int i = 0; i < chooseBankButtons.size(); ++i) {
            bankChooserGroup.add(chooseBankButtons.get(i));
            chooseBankPanel.add(chooseBankButtons.get(i));
        }

        // Enter Number and receive corresponding TAN and copy it to clipboard
        numberField.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 20));
        numberField.setForeground(Color.BLACK);
        numberField.setHorizontalAlignment(JTextField.CENTER);
        numberField.addActionListener(new GetTANListener()); // "Enter"
        numberField.setEnabled(false);
        getTANButton.addActionListener(new GetTANListener()); // Mouseclick
        getTANButton.setEnabled(false);
        tanField.setFont(new Font(Font.MONOSPACED, Font.BOLD, 25));
        tanField.setEditable(false);
        tanField.setHorizontalAlignment(JLabel.CENTER);

        validTANsLabel.setHorizontalAlignment(JLabel.CENTER);

        // Confirm working TAN, so delete it from list
        yesButton.addActionListener(new YesButtonListener());
        yesButton.setForeground(new Color(0, 180, 80)); // R G B
        yesButton.setFont(new Font(Font.DIALOG, Font.BOLD, 20));
        noButton.addActionListener(new NoButtonListener());
        noButton.setForeground(Color.RED);
        noButton.setFont(new Font(Font.DIALOG, Font.BOLD, 20));
        confirmTANPanel.add(askWorkingLabel, BorderLayout.NORTH);
        confirmTANPanel.add(yesButton, BorderLayout.WEST);
        confirmTANPanel.add(noButton, BorderLayout.EAST);
        setConfirmButtonsEnabled(false);

        pane.add(chooseBankPanel);
        pane.add(validTANsLabel);
        pane.add(new JSeparator());
        pane.add(numberField);
        pane.add(getTANButton);
        pane.add(tanField);
        pane.add(new JSeparator());
        pane.add(confirmTANPanel);
        pane.add(new JSeparator());
        pane.add(messageTextView);
    }

    private class RadioButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            // open chosen file with DataManager and print how many valid TANs there are
            try {
                dataManager = new FileManager(getTextOfRadioButton());
            } catch (IOException e1) {
                message(Constants.textbundle.getString("FILE_COULD_NOT_BE_READ")); // TODO FILENAME
            }
            int valids = dataManager.getCountOfValidTANs();
            validTANsLabel.setText(valids + "/" + dataManager.getSize() + " " + Constants.textbundle.getString("TANS_AVAILABLE_FOR") +
                    " " + getTextOfRadioButton().substring(0, getTextOfRadioButton().length() - 4));
            if (valids == 0) {
                validTANsLabel.setForeground(Color.RED);
                numberField.setEnabled(false);
                getTANButton.setEnabled(false);
            } else if (valids > 0 && valids <= Constants.WARNING_NR) {
                validTANsLabel.setForeground(new Color(255, 130, 0));
                numberField.setEnabled(true);
                getTANButton.setEnabled(true);
            } else {
                validTANsLabel.setForeground(new Color(0, 150, 0));
                numberField.setEnabled(true);
                getTANButton.setEnabled(true);
            }
            numberField.setText(Constants.DEFAULT_NUMBERFIELD);
        }

    }

    private class GetTANListener implements ActionListener, KeyListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            if (getTextOfRadioButton() == null) {
                message(Constants.textbundle.getString("NO_BANK_CHOSEN"));
            } else {
                getTAN();
            }
        }

        @Override
        public void keyPressed(KeyEvent arg0) {
            if (arg0.getKeyCode() == KeyEvent.VK_ENTER) {
                getTAN();
            }
        }

        @Override
        public void keyReleased(KeyEvent arg0) {
            // not needed
        }

        @Override
        public void keyTyped(KeyEvent arg0) {
            // not needed
        }

        public void getTAN() {
            Integer nr = null;

            // try to convert input to integer
            try {
                nr = Integer.parseInt(numberField.getText().trim());
            } catch (NumberFormatException ex) {
                message("\"" + numberField.getText() + "\" " + Constants.textbundle.getString("IS_NO_VALID_NUMBER"));
            }

            // conversion successful, now get the corresponding TAN
            if (nr != null) {
                String tan = null;

                /*
                 *  Try to get the content of line # and put it to the String tan. If it didn't work, then print
                 *  a message for the user and return to idle.
                 */
                try {
                    tan = dataManager.getLine(nr);
                } catch (IOException e1) {
                    message(Constants.textbundle.getString("FILE_COULD_NOT_BE_READ") + ": " + getTextOfRadioButton());
                    return;
                } catch (IllegalArgumentException e2) {
                    message(Constants.textbundle.getString("NO_TAN_IN_FILE") + ": " + getTextOfRadioButton() + " -> " + nr.toString());
                    return;
                }

                // Print TAN, if a line was returned that is not flagged as used.
                if (tan != null) {
                    if (!tan.startsWith(Constants.DELETION_FLAG)) {
                        tanField.setText(tan);

                        // copy TAN to clipboard
                        Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(tan), null);

                        setConfirmButtonsEnabled(true);
                        getTANButton.setEnabled(false);

                        message("TAN #" + nr.toString() + " " + Constants.textbundle.getString("WAS_SUCCESSFULLY_COPIED"));
                    } else {
                        // TAN was deleted
                        message("TAN #" + nr.toString() + " " + Constants.textbundle.getString("WAS_ALREADY_USED_ON") + ": " + tan.split(";")[1]);
                    }
                } else {
                    // TAN not found, sending message
                    message(Constants.textbundle.getString("NO_TAN_IN_FILE") + getTextOfRadioButton() + " -> " + nr.toString());
                }
            }
        }
    }

    private class YesButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            if (!tanField.getText().equals(Constants.DEFAULT_TAN)) {
                try {
                    Integer nr = Integer.parseInt(numberField.getText().trim());
                    dataManager.flagLineAsUsed(nr, tanField.getText());
                    message("TAN #" + nr.toString() + " " + Constants.textbundle.getString("WAS_SUCCESSFULLY_MARKED_AS_USED"));
                    resetToBegin();
                } catch (NumberFormatException e1) {
                    throw new Error(); // can't be reached normally because number was already parsed before
                } catch (IOException e2) {
                    message(Constants.textbundle.getString("TAN_WAS_NOT_MARKED_AS_USED_HELP_ADMIN"));
                }
            }

        }

    }

    private class NoButtonListener implements ActionListener {

        /**
         * Resets all elements of gui.
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            resetToBegin();
            message(Constants.textbundle.getString("TAN_MAS_NOT_MARKED_AS_USED"));
        }

    }

    /**
     * Reset the buttons in the bankChooseGroup.
     */
    private void resetChooseBankButtons() {
        bankChooserGroup.clearSelection();
        validTANsLabel.setText(Constants.textbundle.getString("NO_BANK_CHOSEN"));
    }

    /**
     * Returns the text of the currently selected bank in {@code chooseBankButtons}.
     *
     * @return Text of the currently selected radiobutton. This is also the filename of the file.
     */
    private String getTextOfRadioButton() {
        for (JRadioButton jrb : chooseBankButtons) {
            if (jrb.isSelected()) {
                return jrb.getText();
            }
        }
        return null;
    }

    /**
     * Prints the message {@code str} to the {@code messageLabel} as information for the user.
     * 
     * @param str Message for user
     */
    private void message(String str) {
        messageTextView.setText(str + "\n" + messageTextView.getText());
    }

    /**
     * Enables the confirmButtons (yes/no) and makes its panel visible if the parameters is {@code true},
     * invisible and disabled with {@code false}.
     *
     * @param b {@code true}: make confirmPanel visible and enable buttons
     *          {@code false}: make confirmPanel invisible and disable buttons
     */
    private void setConfirmButtonsEnabled(boolean b) {
        yesButton.setEnabled(b);
        noButton.setEnabled(b);
        confirmTANPanel.setVisible(b);
    }

    /**
     * Resets all components to the start of the program.
     */
    private void resetToBegin() {
        numberField.setText(Constants.DEFAULT_NUMBERFIELD);
        numberField.setEnabled(false);
        tanField.setText(Constants.DEFAULT_TAN);
        dataManager = null;
        setConfirmButtonsEnabled(false);
        getTANButton.setEnabled(false);
        validTANsLabel.setForeground(Constants.STANDARD_TEXTCOLOR);
        resetChooseBankButtons();
    }
}
